/***************************************************************************
 *	@file 		fetion_chat.cpp
 *	@Author 	baoyu0125@gmail.com
 *	@create		2011-7
 *	@version 	1.0.0
 *	@brief		definition of functions of class Message
 *				function of groups and contacts
 *	@modify		none
 ***************************************************************************/

#include "fetion_util.h"
#include "fetion_chat.h"
#include "fetion_contact.h"
#include "fetion_exception.h"
#include "fetion_user.h"
#include "fetion_sip.h"

extern boost::mutex user_mutex;
extern char inputbuf[INPUTBUFSIZE];

void Message::display()
{
	char time[30];
	std::string sid;
	char tmp[INPUTBUFSIZE] = {0};

	try {
		strftime(time, sizeof(time), "%H:%M:%S", &sendtime);
		sid = get_sid_from_sipuri(sipuri);
		
		if(sysback) {
			listenString lstr("\nthe message \"%s\" sent at [%s] failed, "
				"please send again.\n", message.c_str(), time);
			stdout<<lstr;//<<sstr;
		}
		else if(sid.length() < 5 || sid == "10000") {
			listenString lstr("\n[%s] system message:\n", time);
			messageString mstr("  %s\n", message.c_str());
			strcpy(tmp, inputbuf);
			memset(inputbuf, 0, sizeof(inputbuf));
			stdout<<lstr;
			strcpy(inputbuf, tmp);
			stdout<<mstr;//<<sstr;
		}
		else {
			if(NULL == contact) {
				throw fetionException("received message from invalid contact.");
			}
			listenString lstr("\n[%s] \"%s\" says:\n", time, contact->get_contact_name().c_str());
			messageString mstr("  %s\n", message.c_str());
			strcpy(tmp, inputbuf);
			memset(inputbuf, 0, sizeof(inputbuf));
			stdout<<lstr;
			strcpy(inputbuf, tmp);
			stdout<<mstr;//<<sstr;
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_WARNING, "display message : %s", ex.what());
	}
}

void Conversation::new_conversation(Contact *ctct, FetionSip *sip)
{
	currentUser *user = currentUser::get_instance();
	
	if(NULL != ctct) {
		contact = ctct;
	}
	if(NULL == sip) {
		currentSip = user->get_sip();
	}
	else {
		currentSip = sip;
	}
}

int Conversation::input_message()
{
	static bool first_time = true;
	char *c;
	std::stringstream msg_buf;
	bool send = true;
	
	if(NULL == contact) {
		errorString estr("no contact is chosen.\n");
		stdout<<estr;
		return FUNC_FAIL;
	}
	if(first_time) {
		sysinfoString sstr("write message, press <Enter> to send, press <Esc> to quit.\n");
		stdout<<sstr;
		first_time = false;
	}
	sysinfoString sstr("to \"%s\"> ", contact->get_contact_name().c_str());
	stdout<<sstr;

	message.clear();
	memset(inputbuf, 0, sizeof(inputbuf));
	strcpy(inputbuf, sstr.get_str());
	c = inputbuf+strlen(inputbuf);
	set_input_attribute();
	while((*c = getchar()) != '\n') {
		if(*c == (char)0x1B) {
			send = false;
			break;
		}
		msg_buf<<*c++;
		if(c == inputbuf+INPUTBUFSIZE) {
			errorString estr("input reached maximum limit size.\n");
			stdout<<estr;
			break;
		}
	}
	memset(inputbuf, 0, sizeof(inputbuf));
	sstr("> ");
	sstr.set_font();
	strcpy(inputbuf, sstr.get_str());
	reset_input_attribute();
	message = msg_buf.str();
	//std::getline(std::cin, message);
	if(send) {
		return FUNC_SUCC;
	}
	else {
		std::cout<<std::endl;
		return FUNC_FAIL;
	}
}

int Conversation::send_message()
{
	struct tm *now;
	Message msg;
	std::string sipmsg;

	WRITE_LOG(LOG_TRACE, "send message \"%s\" to contact \"%s\".", 
		message.c_str(), contact->get_contact_name().c_str());

	currentSip->setType(FetionSip::SIP_MESSAGE);
	currentSip->addEventHeader(SIP_EVENT_CATMESSAGE);
	currentSip->addHeader("T", contact->sipuri);
	currentSip->addHeader("C", "text/plain");
	currentSip->addHeader("K", "SaveHistory");
	
	now = get_currenttime();
	msg.sipuri = contact->sipuri;
	msg.sendtime = *now;
	msg.message = message;
	msg.callid = currentSip->callid;
	unackedList.push_back(msg);

	currentSip->get_send_string(sipmsg, message);
	currentSip->send_sip_message(sipmsg);

	return FUNC_SUCC;
}

int Conversation::send_sms_to_phone()
{
	std::string sipmsg;
	
	WRITE_LOG(LOG_TRACE, "send message \"%s\" to phone of contact \"%s\".",
		message.c_str(), contact->get_contact_name().c_str());

	currentSip->setType(FetionSip::SIP_MESSAGE);
	currentSip->addHeader("T", contact->sipuri);
	currentSip->addEventHeader(SIP_EVENT_SENDCATMESSAGE);

	currentSip->get_send_string(sipmsg, message);
	currentSip->send_sip_message(sipmsg);
	return FUNC_SUCC;
}

int Conversation::send_direct_message(const std::string &to)
{
	std::string sipmsg;
	std::string sipres;
	int code;
	currentUser *user = currentUser::get_instance();
	
	WRITE_LOG(LOG_TRACE, "send direct sms \"%s\" to phone \"%s\".", message.c_str(), to.c_str());
	currentSip->setType(FetionSip::SIP_MESSAGE);
	currentSip->addHeader("T", std::string("tel:")+to);
	currentSip->addHeader("SV", "1");
	currentSip->addEventHeader(SIP_EVENT_SENDDIRECTCATSMS);

	currentSip->get_send_string(sipmsg, message);
	currentSip->send_sip_message(sipmsg);
	if(FUNC_SUCC != currentSip->receive_sip_response(sipres)) {
		throw fetionException("receive sip response failed.");
	}
	code = currentSip->get_status_code(sipres);
	if(code == 420 || code == 421) {
		//user->parse_option_verification(sipres);
	}
	return code;
}

int Conversation::remove_unacked(const int callid)
{
	std::vector<Message>::iterator iter;
	for(iter = unackedList.begin(); iter != unackedList.end();) {
		if(callid == iter->callid) {
			unackedList.erase(iter);
			return FUNC_SUCC;
		}
		else {
			++iter;
		}
	}
	return FUNC_FAIL;
}

int Conversation::check_timeout()
{
	struct tm *now;
	std::vector<Message>::iterator iter;
	time_t now_t, msg_t;
	long seconds;
	
	now = get_currenttime();
	for(iter = unackedList.begin(); iter != unackedList.end();) {
		now_t = mktime(now);
		msg_t = mktime(&iter->sendtime);
		seconds = (long)now_t - (long)msg_t;
		if(seconds > 20) {
			listenString lstr("message \"%s\" send to \"%s\" may be failed.\n", 
				iter->message.c_str(), iter->contact->get_contact_name().c_str());
			stdout<<lstr;
			unackedList.erase(iter);
		}
	}
	return FUNC_SUCC;
}

void Checker::check_unacked(const boost::system::error_code& error)
{
	currentUser *user = currentUser::get_instance();
	boost::mutex::scoped_lock lock(user_mutex);
	user->currentConv->check_timeout();
}
